package controller

import (
	"config"
	"encoding/json"
	"fmt"
	"github.com/gin-gonic/gin"
	"github.com/gorilla/websocket"
	"log"
	"model"
	"net/http"
	"strconv"
	"time"
)

type Client struct {
	Conn   *websocket.Conn
	Name   string
	Uid    int
	RoomID string
	ToUid  int
}

type Message struct {
	MsgType     int8   `json:"msgType"` // -1表示自己的消息 0表示用户发布消息；1表示用户进入；2表示用户退出
	Name        string `json:"name"`
	ContentType string `json:"contentType"` //audio image text video
	Content     string `json:"content"`
	Date        string `json:"date"`
	FromUid     int    `json:"fromUid"`
	ToUid       int    `json:"toUid"`
	roomID      string
	offlineMsg  bool
}

var (
	clients        = make(map[int]Client)
	privateClients = make(map[string]map[int]Client)
	join           = make(chan Client, 10)
	level          = make(chan Client, 10)
	message        = make(chan Message, 10)
	upGrader       = websocket.Upgrader{
		CheckOrigin: func(r *http.Request) bool {
			return true
		},
	}
)

func init() {
	go broadcaster()
}

func broadcaster() {
	for {
		select {
		//消息通道
		case msg := <-message:
			msgType := msg.MsgType
			if msg.roomID != "" {
				//私密聊天处理
				clen := len(privateClients[msg.roomID])
				if clen <= 1 && msgType == 0 {
					//只有一个人 对方不在线
					msgJson, err := json.Marshal(msg)
					if err == nil {
						_, _ = config.Redis.RPush(fmt.Sprintf("%s-%d", msg.roomID, msg.ToUid), msgJson).Result()
					}
				}
				for uid, client := range privateClients[msg.roomID] {
					if msg.FromUid == uid {
						msg.MsgType = -1
					} else {
						msg.MsgType = msgType
					}
					data, err := json.Marshal(msg)
					if err != nil {
						log.Fatal("Fail to marshal message:", err)
						return
					}
					if msg.offlineMsg && msg.MsgType == -1 {
						continue
					}
					if client.Conn.WriteMessage(websocket.TextMessage, data) != nil {
						log.Fatal("Fail to write message")
						return
					}
				}
			} else {
				for uid, client := range clients {
					if msg.FromUid == uid {
						msg.MsgType = -1
					} else {
						msg.MsgType = msgType
					}
					data, err := json.Marshal(msg)
					if err != nil {
						log.Fatal("Fail to marshal message:", err)
						return
					}
					if client.Conn.WriteMessage(websocket.TextMessage, data) != nil {
						log.Fatal("Fail to write message")
						return
					}
				}
			}
			//加入通道
		case client := <-join:
			var msg Message
			var num string
			if client.RoomID != "" {
				if privateClients[client.RoomID] == nil {
					privateClients[client.RoomID] = make(map[int]Client, 2)
				}
				privateClients[client.RoomID][client.Uid] = client
				msg.ToUid = client.ToUid
				msg.roomID = client.RoomID
				num = strconv.Itoa(len(privateClients[client.RoomID]))
			} else {
				clients[client.Uid] = client
				num = strconv.Itoa(len(clients))
			}
			//用户加入成功后广播消息
			msg.Name = client.Name
			msg.MsgType = 1
			msg.FromUid = client.Uid
			msg.Date = time.Now().Format("2006-01-02 15:04:05")
			msg.Content = num
			message <- msg
		case client := <-level:
			var msg Message
			var num string
			if client.RoomID != "" {
				if _, ok := privateClients[client.RoomID][client.Uid]; !ok {
					break
				}
				msg.ToUid = client.ToUid
				msg.roomID = client.RoomID
				delete(privateClients[client.RoomID], client.Uid)
				num = strconv.Itoa(len(privateClients[client.RoomID]))
			} else {
				if _, ok := clients[client.Uid]; !ok {
					break
				}
				delete(clients, client.Uid)
				num = strconv.Itoa(len(clients))
			}
			//用户退出成功后广播消息
			msg.Name = client.Name
			msg.FromUid = client.Uid
			msg.MsgType = 2
			msg.Date = time.Now().Format("2006-01-02 15:04:05")
			msg.Content = num
			message <- msg
		}
	}
}

//公共聊天
func WeChat(c *gin.Context) {
	AuthToken, _ := c.GetQuery("token")
	user := model.User{}
	user.AuthCode = AuthToken
	err := user.GetUserByAuthCode()
	if err != nil {
		return
	}
	conn, err := upGrader.Upgrade(c.Writer, c.Request, nil)
	if err != nil {
		return
	}
	var client Client
	client.Conn = conn
	client.Uid = user.UId
	client.Name = user.Username
	if _, ok := clients[user.UId]; !ok {
		join <- client
	}
	defer func() {
		level <- client
		_ = client.Conn.Close()
	}()

	for {
		_, msgStr, err := client.Conn.ReadMessage()
		if err != nil {
			return
		}
		var msg Message
		if err := json.Unmarshal(msgStr, &msg); err != nil {
			log.Fatal("receive message error:", err.Error())
			return
		}
		msg.MsgType = 0
		msg.Date = time.Now().Format("2006-01-02 15:04:05")
		msg.Name = client.Name
		msg.FromUid = client.Uid
		message <- msg
	}
}

//好友聊天
func PrivateChat(c *gin.Context) {
	AuthToken, _ := c.GetQuery("token")
	var user = model.UserInfo
	user.AuthCode = AuthToken
	err := user.GetUserByAuthCode()
	if err != nil {
		return
	}
	toIdStr, _ := c.GetQuery("toUid")
	ToUid, _ := strconv.Atoi(toIdStr)
	userRelation, err := model.CheckUserRelation(user.UId, ToUid)
	//非好友关系
	if err != nil || userRelation.Type != 1 {
		log.Fatal(err.Error())
		return
	}
	conn, err := upGrader.Upgrade(c.Writer, c.Request, nil)
	if err != nil {
		return
	}
	var client Client
	client.Conn = conn
	client.Uid = user.UId
	client.Name = user.Username
	client.ToUid = ToUid
	client.RoomID = userRelation.RoomID
	if _, ok := privateClients[userRelation.RoomID][user.UId]; !ok {
		join <- client
		//获取未在线时的消息
		msgLen, err := config.Redis.LLen(fmt.Sprintf("%s-%d", userRelation.RoomID, client.Uid)).Result()
		if err == nil {
			var i int64
			var msg Message
			msg.roomID = userRelation.RoomID
			msg.offlineMsg = true
			for i = 0; i < msgLen; i++ {
				msgJson, err := config.Redis.LPop(fmt.Sprintf("%s-%d", userRelation.RoomID, client.Uid)).Result()
				if err == nil {
					_ = json.Unmarshal([]byte(msgJson), &msg)
					message <- msg
				}
			}
		}
	}
	defer func() {
		level <- client
		_ = client.Conn.Close()
	}()
	for {
		_, msgStr, err := client.Conn.ReadMessage()
		if err != nil {
			return
		}
		var msg Message
		if err := json.Unmarshal(msgStr, &msg); err != nil {
			log.Fatal("receive message error:", err.Error())
			return
		}
		msg.MsgType = 0
		msg.Date = time.Now().Format("2006-01-02 15:04:05")
		msg.Name = client.Name
		msg.FromUid = client.Uid
		msg.ToUid = ToUid
		msg.roomID = userRelation.RoomID
		message <- msg
	}
}
